#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 时间     : 2020/11/30 09:14
# 作者     : DL (584054432@qq.com)
# 网址     : https://gitee.com/dingliangPython/
# 软件版本 : Python3.7.3
# 功能     ：

import logging
import os
import time_ext_
from ctypes import byref, c_char, c_char_p, c_double, c_int, c_longlong, c_void_p, cast, CDLL, CFUNCTYPE, POINTER, \
    Structure
from ctypes import windll
from struct import pack, unpack

import win32con


# 装饰器
def method(prototype):
    class MethodDescriptor(object):

        def __init__(self, func):
            self.func = func
            self.bound_funcs = {}  # hold on to references to prevent gc

        def __get__(self, obj, sType = None):
            assert obj is not None  # always require an instance
            try:
                return self.bound_funcs[obj, sType]
            except KeyError:
                ret = self.bound_funcs[obj, sType] = prototype(
                        self.func.__get__(obj, sType))
                return ret

    return MethodDescriptor


# 遥信结构体


class GCL_YX_T(Structure):
    _fields_ = [
            ('address', c_int),
            ('value', c_int),
            ('quality', c_int),
            ('datetime', c_longlong)
            ]


# 遥测结构体


class GCL_YC_T(Structure):
    _fields_ = [
            ('address', c_int),
            ('value', c_double),
            ('quality', c_int),
            ('datetime', c_longlong)
            ]


# 遥文结构体


class GCL_YW_T(Structure):
    _fields_ = [
            ('address', c_int),
            ('value', c_char * 128),
            ('quality', c_int),
            ('datetime', c_longlong)
            ]


# 结构体

class GCL_PSM_ATTACH(Structure):
    _fields_ = [
            ('reason', c_int),
            ('containerId', c_int),
            ('sourceId', c_int),
            ('targetId', c_int),
            ('tag', c_int)
            ]


CALLBACK_SDK_ERR = method(CFUNCTYPE(None, c_int))
CALLBACK_SDK_MSG = method(
        CFUNCTYPE(None, c_char_p, c_char_p, c_int, POINTER(GCL_PSM_ATTACH)))
CALLBACK_SDK_FILE = method(CFUNCTYPE(None, c_char_p, POINTER(GCL_PSM_ATTACH)))
CALLBACK_SDK_RT_REQ = method(CFUNCTYPE(None, POINTER(GCL_PSM_ATTACH)))
CALLBACK_SDK_RT_REV = method(
        CFUNCTYPE(None, c_int, c_void_p, c_int, c_int, POINTER(GCL_PSM_ATTACH)))


# SDK


class GCL_SDK:
    realTimeDatas = {}

    def __init__(self, msg_callback = None, file_callback = None, rt_callback = None, config_path = './'):
        self.log = logging.getLogger("main.sdk")
        self.config_path = config_path
        self.msg_callback = msg_callback
        self.file_callback = file_callback
        self.rt_callback = rt_callback
        self.lib = self.init_lib()
        self.init_callbacks()
        self.gcl_sdk_init()

    def set_msg_callback(self, func):
        # if self.msg_callback and func.__name__ == self.msg_callback.__name__:
        #     return
        # self.log.warning("设置消息回调处理函数:{}".format(func.__name__))
        self.msg_callback = func

    def set_rt_callback(self, func):
        # if self.rt_callback and func.__name__ == self.rt_callback.__name__:
        #     return
        # self.log.warning("设置实时数据回调处理函数:{}".format(func.__name__))
        self.rt_callback = func

    def set_file_callback(self, func):
        # if self.file_callback and func.__name__ == self.file_callback.__name__:
        #     return
        # self.log.warning("设置文件回调处理函数:{}".format(func.__name__))
        self.file_callback = func

    # 初始DLL
    def init_lib(self):
        sdkPath = './gcl_sdk1.dll'
        if not os.path.exists(sdkPath):
            msg = "文件夹：{}，缺少gcl_sdk1.dll文件,不能初始化SDK,即将退出".format(os.path.abspath('./'))
            windll.user32.MessageBoxA(0, msg.encode('gbk'), "警告".encode('gbk'), 1 | win32con.MB_ICONWARNING)
            os._exit(0)
        sdkConfigPath = os.path.join(self.config_path, 'gcl_sdk/config/apps.config.ini')
        if not os.path.exists(sdkConfigPath):
            msg = "sdk配置文件:{},没有找到,无法初始化SDK,请检测配置文件....".format(sdkConfigPath)
            windll.user32.MessageBoxA(0, msg.encode('gbk'), "警告".encode('gbk'), 1 | win32con.MB_ICONWARNING)
            os._exit(0)
        return CDLL(os.path.abspath(sdkPath))

    # 初始化SDK
    def gcl_sdk_init(self):
        self.init()
        if self.gcl_init_status():
            self.log.info("SDK,初始化成功....")
        if self.gcl_terminal_status():
            self.log.info("SDK,连接总线成功...")

    # 初始化状态
    def gcl_init_status(self):
        if self.lib.gci_init_status() > 0:
            return True
        else:
            return False

    # 连接终端状态
    def gcl_terminal_status(self):
        if self.lib.gci_terminal_status() > 0:
            return True
        else:
            return False

    def reg_err_notify(self, fn):
        return self.lib.gci_register_error_notify(fn)

    def unreg_err_notify(self, fn):
        return self.lib.gci_unregister_error_notify(fn)

    def reg_msg_cmd_notify(self, fn):
        return self.lib.gci_register_message_command_notify(fn)

    def unreg_msg_cmd_notify(self, fn):
        return self.lib.gci_unregister_message_command_notify(fn)

    # 注册文件回调
    def reg_file_transfer_notify(self, fn):
        return self.lib.gci_register_file_transfer_notify(fn)

    def unreg_file_transfer_notify(self, fn):
        return self.lib.gci_unregister_file_transfer_notify(fn)

    def reg_file_transfer_result_notify(self, fn):
        return self.lib.gci_register_file_transfer_result_notify(fn)

    def unreg_file_transfer_result_notify(self, fn):
        return self.lib.gci_unregister_file_transfer_result_notify(fn)

    def reg_rt_rev_notify(self, fn):
        return self.lib.gci_register_realtime_data_post_notify(fn)

    def unreg_rt_rev_notify(self, fn):
        return self.lib.gci_unregister_realtime_data_post_notify(fn)

    # 初始化SDK
    def init(self):
        configPathPointer = c_char_p()
        configPathDir = self.config_path + '\\'
        configPathPointer.value = configPathDir.encode('gbk')
        return self.lib.gci_init(1, byref(configPathPointer))

    # 回调注册

    def init_callbacks(self):
        # 注册消息回调
        self.reg_msg_cmd_notify(self.cb_sdk_msg)
        # 注册实时回调
        self.reg_rt_rev_notify(self.cb_sdk_rt_rev)
        # 注册文件回调
        self.reg_file_transfer_notify(self.cb_sdk_Filr)

    # 卸载SDK
    def uninit(self):
        return self.lib.gci_cleanup()

    # 发送消息
    def send_msg(self, cmd, pram):
        cmd = str(cmd).encode('gbk')
        pram = str(pram).encode('gbk')
        lenth = len(pram)
        return self.lib.gci_message_command_send(cmd, pram, lenth, None)

    # 发送消息
    def set_message(self, cmd = 1, message = ""):
        if cmd == 1:
            cmd = 'send.yk.1'
        elif cmd == 2:
            cmd = 'send.yt.1'
        elif cmd == 3:
            cmd = 'send.ys.1'
        receivee = self.send_msg(cmd, message)
        self.log.info('发送rt消息:{},{}'.format(cmd, message))
        return True if receivee >= 0 else None

    # 发送RT数据
    def set_measure(self, measureId, value):
        """

        :param measureId: 测点点号
        :param value: value
        :return:
        """
        measureId = hex(int(measureId))
        obj = None
        sender = None
        # 遥信
        if measureId[2] == '1':
            obj = GCL_YX_T()
            value = int(value)
            sender = self.send_yx
        elif measureId[2] == '2':
            obj = GCL_YC_T()
            value = float(value)
            sender = self.send_yc
        elif measureId[2] == '3':
            obj = GCL_YW_T()
            value = value.encode('utf-8')
            sender = self.send_yw
        obj.address = int(measureId, 16)
        obj.value = value
        obj.quality = 1
        obj.datetime = int(time_ext_.time() * 1000)

        for i in range(5):
            receive = sender(obj)
            if receive != -1:
                return True
        return None

    def get_data(self, *args):

        return self.lib.gci_realtime_data_get(*args)

    def get_data_list(self, *args):
        return self.lib.gci_realtime_data_get_array(*args)

    # 获取RT数据
    def get_measure(self, measureId):
        return self.realTimeDatas.get(measureId)

    # 处理数据
    @staticmethod
    def convert_data(value, sType):
        if sType == 0:
            return [int(v) for v in value]
        if sType == 1:
            return [int(value[0]), float(value[1])]
        if sType == 2:
            return [int(value[0]), value[1].encode('utf-8')]

    # 发送多个RT数据
    def set_measure_list(self, data):
        """
        :param data : [ [mid1,v1],[mid2,v2] ]
        :return: 返回3遥成功数据
        """
        obj_list = [GCL_YX_T, GCL_YC_T, GCL_YW_T]
        receive_list = [None, None, None]
        sender = [self.send_yx_array, self.send_yc_array, self.send_yw_array]
        for i, _ in enumerate(obj_list):
            value = [self.convert_data(v, i) for v in data if hex(int(v[0]))[2] == str(i + 1)]
            values_list = []
            for row in value:
                obj = obj_list[i]()
                obj.address = row[0]
                obj.value = row[1]
                obj.quality = 1
                obj.datetime = int(time_ext_.time() * 1000)
                values_list.append(obj)
            receive_list[i] = sender[i](values_list)
        return receive_list

    def send_yx(self, e):
        buff = pack('=iiiq', e.address, e.value, e.quality, e.datetime)
        return self.lib.gci_realtime_data_post(0x01010203, buff, 20, 1, None)

    def send_yc(self, e):
        buff = pack('=idiq', e.address, e.value, e.quality, e.datetime)
        return self.lib.gci_realtime_data_post(0x0101021C, buff, 24, 1, None)

    def send_yw(self, e):
        buff = pack('=i128siq', e.address, e.value, e.quality, e.datetime)
        return self.lib.gci_realtime_data_post(0x0101022F, buff, 144, 1, None)

    def send_yx_array(self, v):
        if len(v) > 0:
            buff = b''
            for e in v:
                buff += (pack('=iiiq', e.address, e.value, e.quality, e.datetime))
            receive = self.lib.gci_realtime_data_set_list(0x01010203, buff, 20 * len(v), len(v), None)
            return len(v) if receive > 0 else receive
        return None

    def send_yc_array(self, v):
        if len(v) > 0:
            buff = b''
            for e in v:
                buff += (pack('=idiq', e.address, e.value, e.quality, e.datetime))
            receive = self.lib.gci_realtime_data_set_list(0x0101021C, buff, 24 * len(v), len(v), None)
            return len(v) if receive > 0 else receive
        return None

    def send_yw_array(self, v):
        if len(v) > 0:
            receive = 0
            for e in v:
                receive += 1 if self.send_yw(e) > 0 else 0
            return receive
        return None

    @CALLBACK_SDK_ERR
    def cb_sdk_error(self, err):
        self.log.error("异常：{}".format(err))

    # 消息回调处理函数
    @CALLBACK_SDK_MSG
    def cb_sdk_msg(self, cmd, pram, length, attach):
        sourceId = attach[0].sourceId
        targetId = attach[0].targetId
        command = cmd.decode('utf-8')
        message = pram.decode('utf-8')
        length = length
        self.log.info("接收到终端源=0x{:0>8X},目标=0x{:0>8X},命令=\"{}\",消息=\"{}\"".format(sourceId, targetId, command, message))
        if self.msg_callback:
            self.msg_callback(command, message, length, sourceId, targetId)

    # 主动发送文件
    def send_file(self, filePath):
        filePath = filePath.encode('utf-8')
        receive = self.lib.gci_file_transfer_send(filePath, 0)
        if receive > 0:
            self.log.info("发送文件:{},到终端:{},成功".format(filePath, '总线'))
            return True
        info = {
                -1: '链路打开不成功；',
                -2: '没有缓存了',
                -3: "文件不存在",
                -4: "加载不成功",
                0: "文件太大或其它异常",
                }
        self.log.warning(info.get(receive))

    # 文件回调处理
    @CALLBACK_SDK_FILE
    def cb_sdk_Filr(self, file, attach):
        sourceId = attach[0].sourceId
        targetId = attach[0].targetId
        filePath = file.decode('utf-8')
        # 存储到本地
        with open(filePath, 'rb') as f:
            file = f.read()
            fileDir, fileName = os.path.split(filePath)
            file_dir_path = os.path.join(self.config_path, 'temp')
            if not os.path.exists(file_dir_path):
                os.mkdir(file_dir_path)
            path = os.path.join(file_dir_path, fileName)
            with open(path, 'wb') as w:
                w.write(file)
                self.log.warning("文件:{},写入成功".format(path))
        if self.file_callback:
            self.file_callback(filePath, sourceId, targetId)

    @CALLBACK_SDK_RT_REQ
    def cb_sdk_rt_req(self, attach):
        # print(attach)
        pass

    # 实时数据接收回调
    @CALLBACK_SDK_RT_REV
    def cb_sdk_rt_rev(self, rt_rtype, rt_data, size, count, attach):
        id_list = [attach[0].targetId, attach[0].sourceId]
        x = cast(rt_data, POINTER(c_char * size))
        # 报文
        x = bytes([v[0] for v in x.contents])
        # 长度
        step = int(size / count)
        if rt_rtype == 0x01010203 and count > 0:
            for i in range(0, size, step):
                value = unpack('=iiiq', x[i: i + step])
                value = list(value)
                value[3] = value[3] / 1000
                value += id_list
                self.realTimeDatas.update({value[0]: value})
                if self.rt_callback:
                    self.rt_callback(value)

        elif rt_rtype == 0x0101021C and count > 0:
            for i in range(0, size, step):
                value = unpack('=idiq', x[i: i + step])
                value = list(value)
                value[3] = value[3] / 1000
                value += id_list
                self.realTimeDatas.update({value[0]: value})
                if self.rt_callback:
                    self.rt_callback(value)

        elif rt_rtype == 0x0101022F and count > 0:
            for i in range(0, size, step):
                value = unpack('=i128siq', x[i: i + step])
                value = list(value)
                value[3] = value[3] / 1000
                value += id_list
                try:
                    value[1] = value[1].decode('utf', 'ignore').replace('\x00', '')
                except:
                    pass
                self.realTimeDatas.update({value[0]: value})
                if self.rt_callback:
                    self.rt_callback(value)


if __name__ == '__main__':
    def work(a, b, c, d, e):
        print(1111111111, a, b, c, d, e)


    def file_word(filePaht, c, s):
        print("文件路径:{},源:{:X},目:{:#X}".format(filePaht, c, s))


    def rt_back(value):
        print("接收实时数据:{}".format(value))


    sdk = GCL_SDK(msg_callback = None, file_callback = None, rt_callback = None)
    # sdk.init()
    # sdk.gci_terminal_status()
    sdk.set_file_callback(file_word)
    while True:
        print(time_ext_.time())
        time_ext_.sleep(5)
        # sdk.gci_terminal_status()
        # print(1111, sdk.lib.gci_terminal_info())
        # filePath =r'D:\CODE\efl\20200423002343011_00201.efl'
        # xx=sdk.send_file(filePath)
        # print(int(time.time()),xx,444444)
        # sdk.set_measure(16777222, int(time.time()))
        data = [
                [0x01000007, 2],
                [0x01000008, 4],
                [0x02000007, 6.2],
                [0x02000008, 100.6],
                [0x03000007, '中国人之'],
                [0x03000008, '会才'],
                ]

        # xx = sdk.set_measure_list(data)
        # xx=sdk.set_measure(0x03000007,'中国人')
        # print(111111111, sdk.realTimeDatas)
        sdk.set_file_callback(print)